package com.frame.project.modules.test.testing.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;

/**
 * http://localhost:8080/springboot-frame/activiti/create
 * 在绘制流程完成后，访问http://localhost:8080/springboot-frame/activiti/deploy?modelId=1 对该流程进行部署

http://localhost:8080/springboot-frame/activiti/start?keyName=hello 启动流程

http://localhost:8080/springboot-frame/activiti/run?processInstanceId=1 提交
 * @author zc 2018-06-04
 */
@RestController
@RequestMapping("/activiti")
public class ActivitiController {

	private static final Logger log = LogManager.getLogger(ActivitiController.class);

	
	@Autowired
	private RepositoryService repositoryService; //管理流程定义
	
	@Autowired
	private RuntimeService runtimeService; //执行管理，包括启动、推进、删除流程实例等操作
	
	@Autowired
	private TaskService taskService; //任务管理
	
	//@Autowired
	//private HistoryService historyService; //历史管理(执行完的数据的管理)
	
	//@Autowired
	//private IdentityService identityService; //组织机构管理
	
	@Autowired
	ProcessEngine processEngine; //核心API,其他的类都是由他而来
	
	@Autowired
	ObjectMapper objectMapper;


	/**
	 * 新建一个空模型
	 */
	@RequestMapping("/create")
	public void newModel(HttpServletRequest request,HttpServletResponse response){
		try {
			ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

			RepositoryService repositoryService = processEngine.getRepositoryService();

			ObjectMapper objectMapper = new ObjectMapper();
			ObjectNode editorNode = objectMapper.createObjectNode();
			editorNode.put("id", "canvas");
			editorNode.put("resourceId", "canvas");
			ObjectNode stencilSetNode = objectMapper.createObjectNode();
			stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
			editorNode.put("stencilset", stencilSetNode);
			Model modelData = repositoryService.newModel();

			ObjectNode modelObjectNode = objectMapper.createObjectNode();
			modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, "hello1111");
			modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
			String description = "hello1111";
			modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
			modelData.setMetaInfo(modelObjectNode.toString());
			modelData.setName("hello1111");
			modelData.setKey("12313123");

			//保存模型
			repositoryService.saveModel(modelData);
			repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
			response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + modelData.getId());
		} catch (Exception e) {
			System.out.println("创建模型失败：");
		}

	}

	/**
	 * 获取所有模型
	 */
	@RequestMapping("/modelList")
	public Object modelList(){
		RepositoryService repositoryService = processEngine.getRepositoryService();
		return repositoryService.createModelQuery().list();
	}

	/**
	 * 发布模型为流程定义
	 */
	@RequestMapping("/deploy")
	public Object deploy(String modelId) throws Exception {

		//获取模型
		RepositoryService repositoryService = processEngine.getRepositoryService();
		Model modelData = repositoryService.getModel(modelId);
		byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

		if (bytes == null) {
			return "模型数据为空，请先设计流程并成功保存，再进行发布。";
		}

		JsonNode modelNode = new ObjectMapper().readTree(bytes);

		BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
		if(model.getProcesses().size()==0){
			return "数据模型不符要求，请至少设计一条主线流程。";
		}
		byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

		//发布流程
		String processName = modelData.getName() + ".bpmn20.xml";
		Deployment deployment = repositoryService.createDeployment()
				.name(modelData.getName())
				.addString(processName, new String(bpmnBytes, "UTF-8"))
				.deploy();
		modelData.setDeploymentId(deployment.getId());
		repositoryService.saveModel(modelData);

		return "SUCCESS";
	}

	/**
	 *  启动流程
	 */
	@RequestMapping("/start")
	public Object startProcess(String keyName) {
		ProcessInstance process = processEngine.getRuntimeService().startProcessInstanceByKey(keyName);

		return process.getId() + " : " + process.getProcessDefinitionId();
	}

	/**
	 *  提交任务
	 */
	@RequestMapping("/run")
	public Object run(String processInstanceId) {
		Task task = processEngine.getTaskService().createTaskQuery().processInstanceId(processInstanceId).singleResult();

		log.info("task {} find ", task.getId());
		processEngine.getTaskService().complete(task.getId());
		return "SUCCESS";
	}






	@RequestMapping("helloWorld") 
	public void helloWorld() {  

		//根据bpmn文件部署流程  
		Deployment deploy = repositoryService.createDeployment()
				.addClasspathResource("processes/TestProcess.bpmn")
				.deploy();  
		//获取流程定义  
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();  
		//启动流程定义，返回流程实例  
		ProcessInstance pi = runtimeService.startProcessInstanceById(processDefinition.getId());  
		String processId = pi.getId();  
		System.out.println("流程创建成功，当前流程实例ID："+processId);  

		Task task=taskService.createTaskQuery().processInstanceId(processId).singleResult();  
		System.out.println("执行前，任务名称："+task.getName());  
		taskService.complete(task.getId());  

		task = taskService.createTaskQuery().processInstanceId(processId).singleResult();  
		System.out.println("task为null，任务执行完毕："+task);  
	}

	@RequestMapping("singleAssignee")
	public void setSingleAssignee() {  

		//根据bpmn文件部署流程  
		repositoryService.createDeployment().addClasspathResource("processes/singleAssignee.bpmn").deploy();
		// 设置User Task1受理人变量
		Map<String, Object> variables = new HashMap<>();
		variables.put("user1", "007");
		//采用key来启动流程定义并设置流程变量，返回流程实例  
		ProcessInstance pi = runtimeService.startProcessInstanceByKey("singleAssignee", variables);  
		String processId = pi.getId();  
		System.out.println("流程创建成功，当前流程实例ID："+processId);

		// 注意 这里需要拿007来查询，key-value需要拿value来获取任务
		List<Task> list = taskService.createTaskQuery().taskAssignee("007").list();
		if(list!=null && list.size()>0){ 
			for(org.activiti.engine.task.Task task:list){  
				System.out.println("任务ID："+task.getId());  
				System.out.println("任务的办理人："+task.getAssignee());  
				System.out.println("任务名称："+task.getName());  
				System.out.println("任务的创建时间："+task.getCreateTime());  
				System.out.println("流程实例ID："+task.getProcessInstanceId());  
				System.out.println("#######################################");
			}
		}

		// 设置User Task2的受理人变量
		Map<String, Object> variables1 = new HashMap<>();
		variables1.put("user2", "Kevin");
		// 因为007只有一个代办的任务，直接完成任务，并赋值下一个节点的受理人user2为Kevin办理
		taskService.complete(list.get(0).getId(), variables1);
		System.out.println("User Task1被完成了，此时流程已流转到User Task2");
	}

	@RequestMapping("multiAssignee")
	public void setMultiAssignee() {  

		//根据bpmn文件部署流程  
		repositoryService.createDeployment().addClasspathResource("processes/MultiAssignee.bpmn").deploy();
		// 设置多个处理人变量 这里设置了三个人
		Map<String, Object> variables = new HashMap<>();
		List<String> userList = new ArrayList<>();
		userList.add("user1");
		userList.add("user2");
		userList.add("user3");
		variables.put("userList", userList);
		//采用key来启动流程定义并设置流程变量，返回流程实例  
		ProcessInstance pi = runtimeService.startProcessInstanceByKey("multiAssigneeProcess", variables);  
		String processId = pi.getId();  
		System.out.println("流程创建成功，当前流程实例ID："+processId);

		// 查看user1的任务
		List<Task> list = taskService.createTaskQuery().taskAssignee("user1").list();
		if(list!=null && list.size()>0){ 
			for(org.activiti.engine.task.Task task:list){  
				System.out.println("任务ID："+task.getId());  
				System.out.println("任务的办理人："+task.getAssignee());  
				System.out.println("任务名称："+task.getName());  
				System.out.println("任务的创建时间："+task.getCreateTime());  
				System.out.println("流程实例ID："+task.getProcessInstanceId());  
				System.out.println("#######################################");
			}
		}

		// 查看user2的任务
		List<Task> list2 = taskService.createTaskQuery().taskAssignee("user2").list();
		if(list2!=null && list2.size()>0){ 
			for(org.activiti.engine.task.Task task:list2){  
				System.out.println("任务ID："+task.getId());  
				System.out.println("任务的办理人："+task.getAssignee());  
				System.out.println("任务名称："+task.getName());  
				System.out.println("任务的创建时间："+task.getCreateTime());  
				System.out.println("流程实例ID："+task.getProcessInstanceId());  
				System.out.println("#######################################");
			}
		}

		// 查看user3的任务
		List<Task> list3 = taskService.createTaskQuery().taskAssignee("user3").list();
		if(list3!=null && list3.size()>0){ 
			for(org.activiti.engine.task.Task task:list3){  
				System.out.println("任务ID："+task.getId());  
				System.out.println("任务的办理人："+task.getAssignee());  
				System.out.println("任务名称："+task.getName());  
				System.out.println("任务的创建时间："+task.getCreateTime());  
				System.out.println("流程实例ID："+task.getProcessInstanceId());  
				System.out.println("#######################################");
			}
		}

	}

	@RequestMapping("exclusiveGateway")
	public void exclusiveGateway() {  

		//根据bpmn文件部署流程  
		repositoryService.createDeployment().addClasspathResource("processes/exclusiveGateway.bpmn").deploy();
		// 设置User Task1受理人变量
		Map<String, Object> variables = new HashMap<>();
		variables.put("user1", "007");
		//采用key来启动流程定义并设置流程变量，返回流程实例  
		ProcessInstance pi = runtimeService.startProcessInstanceByKey("exclusiveGatewayAndTimerBoundaryEventProcess", variables);  
		String processId = pi.getId();  
		System.out.println("流程创建成功，当前流程实例ID："+processId);

		// 注意 这里需要拿007来查询，key-value需要拿value来获取任务
		List<Task> list = taskService.createTaskQuery().taskAssignee("007").list();
		Map<String, Object> variables1 = new HashMap<>();
		variables1.put("user2", "lili"); // 设置User Task2的受理人变量
		variables1.put("operate", ""); // 设置用户的操作 为空 表示走flow3的默认路线
		taskService.complete(list.get(0).getId(), variables1);
		System.out.println("User Task1被完成了，此时流程已流转到User Task2");

		List<Task> list1 = taskService.createTaskQuery().taskAssignee("lili").list();
		Map<String, Object> variables2 = new HashMap<>();
		variables2.put("user4", "bobo");
		variables2.put("startTime", "2018-6-11T14:22:00"); // 设置定时边界任务的触发时间 注意：后面的时间必须是ISO 8601时间格式的字符串！！！
		taskService.complete(list1.get(0).getId(), variables2);

		List<Task> list2 = taskService.createTaskQuery().taskAssignee("bobo").list();
		if(list2!=null && list2.size()>0){ 
			for(org.activiti.engine.task.Task task:list2){  
				System.out.println("任务ID："+task.getId());  
				System.out.println("任务的办理人："+task.getAssignee());  
				System.out.println("任务名称："+task.getName());  
				System.out.println("任务的创建时间："+task.getCreateTime());  
				System.out.println("流程实例ID："+task.getProcessInstanceId());  
				System.out.println("#######################################");
			}
		}
	}
	
	
	
	/**
	 * 查询流程当前节点的下一步节点。用于流程提示时的提示。
	 * @param taskId
	 * @return
	 * @throws Exception
	 */
	public Map<String,  FlowNode> findNextTask(String taskId) throws Exception{
		Map<String, org.activiti.bpmn.model.FlowNode> nodeMap = new HashMap<String, org.activiti.bpmn.model.FlowNode>();
		ProcessInstance processInstance =  findProcessInstanceByTaskId(taskId);
		//查询当前节点
		HistoricTaskInstance histask = findHistricTaskById(taskId,processInstance.getProcessInstanceId());
		//查询流程定义 。
		BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(processInstance.getProcessDefinitionId());
		List<org.activiti.bpmn.model.Process> listp = bpmnModel.getProcesses();
		org.activiti.bpmn.model.Process process = listp.get(0);
		//当前节点流定义
		FlowNode sourceFlowElement = ( FlowNode) process.getFlowElement(histask.getTaskDefinitionKey());
		//找到当前任务的流程变量
		List<HistoricVariableInstance> listVar= processEngine.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId()).list() ; 
		iteratorNextNodes(process, sourceFlowElement, nodeMap,listVar);
		return nodeMap;
	}


	/**
	 * 查询历史
	 * @param taskId			任务id
	 * @param processInstanceId 流程实例id
	 * @return
	 * @throws Exception
	 */
	public  HistoricTaskInstance findHistricTaskById(String taskId, String processInstanceId)  
			throws Exception {  
		List<HistoricTaskInstance> list = processEngine.getHistoryService()
				.createHistoricTaskInstanceQuery()
				.processInstanceId(processInstanceId)
				.list();
		if(list!=null && list.size()>0){
			for(HistoricTaskInstance hti:list){
				if(hti.getParentTaskId().equals(taskId)) {
					return hti;
				}
			}
		}

		return null;  
	}  



	/** 
	 * 根据任务ID获取对应的流程实例 
	 *  
	 * @param taskId 
	 *            任务ID 
	 * @return 
	 * @throws Exception 
	 */  
	public  ProcessInstance findProcessInstanceByTaskId(String taskId)  
			throws Exception {  
		// 找到流程实例  
		ProcessInstance processInstance = processEngine.getRuntimeService()
				.createProcessInstanceQuery().processInstanceId(  
						findTaskById(taskId).getProcessInstanceId())  
				.singleResult();  
		if (processInstance == null) {   
			throw new Exception("流程实例未找到!");  
		}  
		return processInstance;  
	}  

	/** 
	 * 根据任务ID获得任务实例 
	 *  
	 * @param taskId 
	 *  任务ID 
	 * @return 
	 * @throws Exception 
	 */  
	private  TaskEntity findTaskById(String taskId) throws Exception {  
		TaskEntity task = (TaskEntity) processEngine.getTaskService().createTaskQuery().taskId(  
				taskId).singleResult();  
		if (task == null) {  
			throw new Exception("任务实例未找到!");  
		}  
		return task;  
	}  


	/**
	 * 查询流程当前节点的下一步节点。用于流程提示时的提示。
	 * @param process
	 * @param sourceFlowElement
	 * @param nodeMap
	 * @param listVar
	 * @throws Exception
	 */
	private void iteratorNextNodes(org.activiti.bpmn.model.Process process, FlowNode sourceFlowElement, Map<String,  FlowNode> nodeMap,List<HistoricVariableInstance> listVar)
			throws Exception {
		List<SequenceFlow> list = sourceFlowElement.getOutgoingFlows();
		for (SequenceFlow sf : list) {
			sourceFlowElement = ( FlowNode) process.getFlowElement( sf.getTargetRef());
			if(StringUtils.isNotEmpty(sf.getConditionExpression() )){
				ExpressionFactory factory = new ExpressionFactoryImpl();  
				SimpleContext context = new SimpleContext();  
				for(HistoricVariableInstance var :listVar){
					context.setVariable(var.getVariableName(), factory.createValueExpression(var.getValue(), var.getValue().getClass()));
				}
				ValueExpression e = factory.createValueExpression(context, sf.getConditionExpression(), boolean.class);  
				if((Boolean)e.getValue(context)){
					nodeMap.put(sourceFlowElement.getId(), sourceFlowElement);
					break;
				}
			}
			if (sourceFlowElement instanceof org.activiti.bpmn.model.UserTask) {
				nodeMap.put(sourceFlowElement.getId(), sourceFlowElement);
				break;
			}else if (sourceFlowElement instanceof org.activiti.bpmn.model.ExclusiveGateway) { 
				iteratorNextNodes(process, sourceFlowElement, nodeMap,listVar);
			}
		}
	}
	
	
	

}
